package org.bdware.registry.handler;

import org.apache.log4j.Logger;
import org.bdware.doip.core.doipMessage.DoipMessage;
import org.bdware.doip.core.doipMessage.DoipResponseCode;
import org.bdware.doip.core.model.digitalObject.DigitalObject;
import org.bdware.doip.core.model.digitalObject.DoType;
import org.bdware.doip.core.model.digitalObject.Element;
import org.bdware.doip.core.model.metadata.SearchParameter;
import org.bdware.doip.core.model.metadata.SearchResult;
import org.bdware.doip.core.utils.DoipGson;
import org.bdware.doip.endpoint.doipServer.DoipServiceInfo;
import org.bdware.doip.endpoint.doipServer.RegistryHandlerBase;
import org.bdware.doip.implementation.DoIndexer;
import org.bdware.doip.implementation.DoStorage;

import java.io.IOException;

public class SimpleRegistryHandler extends RegistryHandlerBase {

    Logger logger = Logger.getLogger(SimpleRegistryHandler.class);
    DoStorage store;
    DoIndexer indexer;

    public SimpleRegistryHandler(DoipServiceInfo info, DoStorage s, DoIndexer i){
        super(info);
        this.serviceInfo = info;
        store = s;
        indexer = i;
    }

    @Override
    public DoipMessage handleHello(DoipMessage request) {
        return replyDoipServiceInfo(request);
    }

    @Override
    public DoipMessage handleListOps(DoipMessage request) {
        return replyAllOperations(request);
    }

    @Override
    public DoipMessage handleCreate(DoipMessage request) {
        try {
            DigitalObject digitalObject = request.body.getDataAsDigitalObject();
            if(digitalObject.type != DoType.Metadata)
                return replyStringWithStatus(request, "Invalid DO Type: Only 0.TYPE/DO.Metadata is accepted.", DoipResponseCode.Invalid);
            if (!request.header.parameters.id.equals(serviceInfo.id)) {
                return replyStringWithStatus(request, "Wrong operation targetID, should be: " + serviceInfo.id, DoipResponseCode.Invalid);
            }
            DigitalObject ret = store.getDoByID(digitalObject.id);
            if (ret != null) {
                return replyStringWithStatus(request, "Failed: the metadata of this DO is already existed.", DoipResponseCode.DoAlreadyExist);
            }
            logger.debug("Message body length: " + request.body.getLength());
            logger.info("create DO identifier : " + digitalObject.id);
            store.storeDo(digitalObject);
            indexer.indexDigitalObject(digitalObject);
            if (digitalObject.elements != null)
                for (Element e : digitalObject.elements) {
                    e.excludeData();
                }
            return replyDO(request,digitalObject);
        } catch (Exception e) {
            e.printStackTrace();
            return replyStringWithStatus(request, "Server Exception: " + e.getMessage(), DoipResponseCode.MoreThanOneErrors);
        }
    }

    @Override
    public DoipMessage handleUpdate(DoipMessage request) {
        try {
            DigitalObject digitalObject = request.body.getDataAsDigitalObject();
            if (!request.header.parameters.id.equals(digitalObject.id)) {
                return replyStringWithStatus(request, "Invalid request: different identifiers in DO and message header.", DoipResponseCode.Invalid);
            }
            DigitalObject ret = store.getDoByID(digitalObject.id);
            if (ret == null) {
                return replyStringWithStatus(request, "Failed: DO is not existed.", DoipResponseCode.DoNotFound);
            }
            store.updateDo(digitalObject);
            indexer.deleteDigitalObject(digitalObject.id);
            indexer.indexDigitalObject(digitalObject);
            if (digitalObject.elements != null)
                for (Element e : digitalObject.elements) {
                    e.excludeData();
                }
            return replyDO(request,digitalObject);
        } catch (Exception e) {
            e.printStackTrace();
            return replyStringWithStatus(request, "Server Exception: " + e.getMessage(), DoipResponseCode.MoreThanOneErrors);
        }
    }

    @Override
    public DoipMessage handleDelete(DoipMessage request) {
        try {
            DigitalObject ret = store.getDoByID(request.header.parameters.id);
            if (ret == null) {
                return replyStringWithStatus(request, "Failed: DO is not existed.", DoipResponseCode.DoNotFound);
            }
            store.deleteDoByID(request.header.parameters.id);
            indexer.deleteDigitalObject(request.header.parameters.id);
            return replyNull(request);
        }catch (Exception e){
            e.printStackTrace();
            return replyStringWithStatus(request, "Server Exception: " + e.getMessage(), DoipResponseCode.MoreThanOneErrors);
        }
    }

    @Override
    public DoipMessage handleRetrieve(DoipMessage request) {
        try {
            DigitalObject ret = store.getDoByID(request.header.parameters.id);
            if (ret == null) {
                return replyStringWithStatus(request, "Failed: DO is not existed.", DoipResponseCode.DoNotFound);
            }
            //如果是获取element
            if (request.header.parameters.attributes != null && request.header.parameters.attributes.has("element")) {
                for (Element e : ret.elements) {
                    if (e.id.equals(request.header.parameters.attributes.get("element").getAsString())) {
                        return replyString(request, e.getData() == null ? "" : new String(e.getData()));
                    }
                }
                return replyStringWithStatus(request,
                        "element not found: " + request.header.parameters.attributes.get("element"),
                        DoipResponseCode.DoNotFound);
            }
            //不需要获取Elementdata
            if (request.header.parameters.attributes == null ||
                    !request.header.parameters.attributes.has("includeElementData")) {
                logger.debug("clean element data");
                for (Element e : ret.elements) {
                    e.excludeData();
                }
            }
            return replyDO(request,ret);
        }catch (Exception e){
            e.printStackTrace();
            return replyStringWithStatus(request, "Server Exception: " + e.getMessage(), DoipResponseCode.MoreThanOneErrors);
        }
    }

    @Override
    public DoipMessage handleUnknown(DoipMessage request) {
        return replyStringWithStatus(request,"Unsupported Operation!", DoipResponseCode.Declined);
    }

    @Override
    public DoipMessage handleSearch(DoipMessage request) {
        if(request.header.parameters.attributes == null ){
            logger.info("invalid search request: attributes not found");
            return replyStringWithStatus(request, "invalid search request: attributes not found.", DoipResponseCode.Invalid);
        }
        SearchParameter sp = new SearchParameter(request.header.parameters.attributes.get("query").getAsString(),
                !request.header.parameters.attributes.has("pageNum")?0:request.header.parameters.attributes.get("pageNum").getAsInt(),
                !request.header.parameters.attributes.has("pageSize")?0:request.header.parameters.attributes.get("pageSize").getAsInt(),
                !request.header.parameters.attributes.has("sortFields")?"":request.header.parameters.attributes.get("sortFields").getAsString(),
                !request.header.parameters.attributes.has("type")?null:request.header.parameters.attributes.get("type").getAsString());
        try {
            SearchResult sr = indexer.search(sp);
            return replyString(request,DoipGson.getDoipGson().toJson(sr));
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }
}
